"""
Dar Maze minigame.

The player is shown a maze for a set time. The lights are turned out and the
player must navigate the maze with a character who carries a torch to see a small
local area only.

"""

#  Copyright 2010 Nick Ryder.

#  This file is part of Minigame Mayhem.

#  Minigame Mayhem is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.

#  Minigame Mayhem is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.

#  You should have received a copy of the GNU General Public License
#  along with Minigame Mayhem.  If not, see <http://www.gnu.org/licenses/>.


import pygame
import os
from pygame.locals import *
import time
import sys
import random
import os
cwd = os.getcwd()
path = os.path.split(cwd)[0]
corepath = os.path.join(path, "core")
datapath = os.path.join(path, "data")
sys.path.append(corepath)
import minigame
import spritesheet

class Maze:

   def __init__(self, mazestring="", filename=""):
      self.mazestring = mazestring
      self.loaded = False
      self.empties = []   # List of empty rects
      self.blocks = []     # List of block rects
      self.height = None
      self.width = None
      self.blockheight = None
      self.blockwidth = None
      self.mazesurface = None
      self.startblock = None
      self.exit = None
      self.start = None
      if self.mazestring != "":
         self.loadmaze(self.mazestring)
      elif filename != "":
         self.loadmazeFromFile(filename)

   def loadimgs(self, filename):
      "Load images of empty square, block, exit and start block."
      self.sprites = spritesheet.SpriteSheet(os.path.join(datapath, "mazesprites.bmp"))
      images = []
      for y in range(2):
         for x in range(2):
            rect = pygame.Rect(x * (self.blockwidth + 1) + 1,
               y * (self.blockheight + 1) + 1, self.blockwidth, self.blockheight)
            images.append(self.sprites.get_image(rect))
      self.blocksurface = pygame.Surface((self.blockwidth, self.blockheight))
      self.blocksurface.blit(images[0], (0, 0))
      self.emptysurface = pygame.Surface((self.blockwidth, self.blockheight))
      self.emptysurface.blit(images[1], (0, 0))
      self.startsurface = pygame.Surface((self.blockwidth, self.blockheight))
      self.startsurface.blit(images[2], (0, 0))
      self.exitsurface = pygame.Surface((self.blockwidth, self.blockheight))
      self.exitsurface.blit(images[3], (0, 0))
      
   def loadmaze(self, mazestring=""):
      "Create maze from mazestring."
      if mazestring != "":
         self.mazestring = mazestring
      if self.mazestring != "":
         # Load the maze
         lines = self.mazestring.split("\n")
         [width, height] = lines[0].split("x")
         self.blockwidth = int(width)
         self.blockheight = int(height)
         lines = lines[1:]
         self.height = len(lines) - 1
         self.width = len(lines[0])
         for y in range(len(lines)):
            for x in range(len(lines[y])):
               if lines[y][x] == "X":
                  self.blocks.append(pygame.Rect(x * self.blockwidth, 
                     y * self.blockheight, self.blockwidth, self.blockheight))
               elif lines[y][x] == "E":
                  self.exit = pygame.Rect(x * self.blockwidth,
                     y * self.blockheight, self.blockwidth, self.blockheight)
               elif lines[y][x] == "S":
                  self.startblock = pygame.Rect(x * self.blockwidth,
                     y * self.blockheight, self.blockwidth, self.blockheight)
               else:
                  self.empties.append(pygame.Rect(x * self.blockwidth, 
                     y * self.blockheight, self.blockwidth, self.blockheight))
         self.loaded = True
               
   def draw(self, screen, rect=None):
      "Draw either whole maze to screen or draw area colliding with rect."
      if rect == None:  # Draw all of maze
         for block in self.blocks:
            screen.blit(self.blocksurface, block)
         for empty in self.empties:
            screen.blit(self.emptysurface, empty)
         screen.blit(self.exitsurface, self.exit)
         screen.blit(self.startsurface, self.startblock)
      else:    # Draw only parts colliding with rect.
         for block in self.blocks:
            if rect.colliderect(block):
               screen.blit(self.blocksurface, block)
         for empty in self.empties:
            if rect.colliderect(empty):
               screen.blit(self.emptysurface, empty)
         if rect.colliderect(self.exit):
            screen.blit(self.exitsurface, self.exit)
         if rect.colliderect(self.startblock):
            screen.blit(self.startsurface, self.startblock)
            
   def loadmazeFromFile(self, filename):
      filename = os.path.join(datapath, filename)
      inp = open(filename, "r")
      string = inp.read()
      inp.close()
      self.loadmaze(string)

class Player:

   def __init__(self, maze):
      self.maze = maze
      self.rect = pygame.Rect(1, 2 * (self.maze.blockheight+ 1) + 1,
          self.maze.blockwidth, self.maze.blockheight)
      self.colliderect = pygame.Rect(0, 0, int(0.7 * self.rect.width), int(0.7*self.rect.height))
      self.surface = pygame.Surface((self.maze.blockwidth, self.maze.blockheight))
      self.surface = maze.sprites.get_image(self.rect, -1)
      self.rect.center = maze.startblock.center
      self.colliderect.center = self.rect.center
      self.speed = 0.08    # pixels per milisecond
      self.movingright = 0    # 1 for moving right, -1 for moving left
      self.movingdown = 0    # 1 for moving down, -1 for moving up
      self.lastupdate = None

   def update(self, screen, hider=None):
      if self.lastupdate == None:
         self.lastupdate = time.time()
         screen.blit(self.surface, self.rect)
         return
      if self.movingright != 0 or self.movingdown != 0:
         if hider != None:
            self.maze.draw(screen)
         else:
            self.maze.draw(screen, self.rect)
         self.move()
         screen.blit(self.surface, self.rect)
      self.lastupdate = time.time()

   def move(self):
      dt = time.time() - self.lastupdate
      if self.movingright != 0 and self.movingdown != 0:
         speed = self.speed * 2**-0.5
      dx = int(self.movingright * self.speed * dt * 1000)
      dy = (self.movingdown* self.speed * dt * 1000)
      if dx != 0:
         self.colliderect.x += dx
         for block in self.maze.blocks:
            if block.colliderect(self.colliderect):
               if dx > 0:
                  self.colliderect.right = block.left
               elif dx < 0:
                  self.colliderect.left = block.right
      if dy != 0:
         self.colliderect.y += dy
         for block in self.maze.blocks:
            if block.colliderect(self.colliderect):
               if dy > 0:
                  self.colliderect.bottom = block.top
               elif dy < 0:
                  self.colliderect.top = block.bottom
      self.rect.center = self.colliderect.center


class DarkMazeMinigame(minigame.Minigame):

   def __init__(self, screen, level=1):
      minigame.Minigame.__init__(self, screen, level)
      self.developers = ["Nick Ryder"]
      self.maze = None
      self.player = None
      self.showtime = 10
      self.mazetime = 30
      self.hider = None
      self.background = pygame.Surface((self.screen.get_width(), 
         self.screen.get_height()))
      self.backgroundcolour = (77, 77, 0)
      self.background.fill(self.backgroundcolour)

   def start(self):
      "Load maze, images, etc."
      self.screen.blit(self.background, (0, 0))
      self.maze = Maze(filename="testmaze")
      self.maze.loadimgs("mazesprites.bmp")
      self.maze.draw(self.screen)
      self.player = Player(self.maze)
      self.infofont = pygame.font.Font(None, 30)
      self.timesurf = pygame.Surface(self.infofont.size("Time Left: 30s"))
      self.timesurf.fill(self.backgroundcolour)
      self.timerect = self.timesurf.get_rect(right = self.screen.get_width(),
         bottom = self.screen.get_height())
      self.hider = pygame.Surface((2 * self.maze.width * self.maze.blockwidth,
         2 * self.maze.height * self.maze.blockheight))
      self.hider.fill((0, 0, 0))
      pygame.draw.circle(self.hider, (255, 255, 255), (self.hider.get_width()/2, 
         self.hider.get_height()/2), 100)
      self.hider.set_colorkey((255, 255, 255))
      self.hiderrect = self.hider.get_rect()

   def main(self):
      self.phase_one()
      self.phase_two()

   def phase_one(self):
      "Show the maze to the player."
      starttime = time.time()
      currenttime = time.time()
      timeleft = self.showtime - (currenttime - starttime)
      while timeleft > 0:
         for event in pygame.event.get():
            if event.type == QUIT:
               sys.exit()
            elif event.type == KEYDOWN:
               if event.key == K_ESCAPE:
                  sys.exit()
         currenttime = time.time()
         timeleft = self.showtime - (currenttime - starttime)
         timemsg = "Time left: %ds" % (timeleft + 1)
         timemsg = self.infofont.render(timemsg, 1, (0, 0, 0))
         self.screen.blit(self.timesurf, self.timerect)
         self.screen.blit(timemsg, self.timerect)
         pygame.display.flip()

   def phase_two(self):
      "The player tries to navigate the maze."
      self.player.update(self.screen)
      starttime = time.time()
      currenttime = time.time()
      timeleft = self.mazetime - (currenttime - starttime)
      wintime = None
      while timeleft > 0:
         currenttime = time.time()
         timeleft = self.mazetime - (currenttime - starttime)
         timemsg = "Time left: %ds" % (timeleft + 1)
         timemsg = self.infofont.render(timemsg, 1, (0, 0, 0))
         self.screen.blit(self.timesurf, self.timerect)
         self.screen.blit(timemsg, self.timerect)
         for event in pygame.event.get():
            if event.type == QUIT:
               sys.exit()
            elif event.type == KEYDOWN:
               if event.key == K_ESCAPE:
                  sys.exit()
               elif event.key == K_DOWN:
                  self.player.movingdown = 1
               elif event.key == K_UP:
                  self.player.movingdown = -1
               elif event.key == K_LEFT:
                  self.player.movingright = -1
               elif event.key == K_RIGHT:
                  self.player.movingright = 1
            elif event.type == KEYUP:
               if self.player.movingdown == 1 and event.key == K_DOWN:
                  self.player.movingdown = 0
               elif self.player.movingdown == -1 and event.key == K_UP:
                  self.player.movingdown = 0
               elif self.player.movingright == 1 and event.key == K_RIGHT:
                  self.player.movingright= 0
               elif self.player.movingright == -1 and event.key == K_LEFT:
                  self.player.movingright = 0
         self.player.update(self.screen, self.hider)
         #self.hiderrect.center = self.player.rect.center
         width = self.maze.width * self.maze.blockwidth 
         height = self.maze.height * self.maze.blockheight
         posx = width - self.player.rect.center[0]
         posy = height - self.player.rect.center[1]
         drawrect = (posx, posy, width, height)
         screen.blit(self.hider, self.hiderrect, drawrect)
         pygame.display.flip()
         pygame.time.wait(10)
         if self.player.colliderect.colliderect(self.maze.exit) and self.win !=True:
            self.win = True
            wintime = time.time()
         if wintime != None:
            if currenttime - wintime > 1:
               timeleft = -1
      

   def finish(self):
      if self.win:
         print "You win!"
      else:
         print "You lose!"

if __name__ == "__main__":
   pygame.init()
   screen = pygame.display.set_mode((800, 600))
   pygame.display.set_caption("Dark Maze Minigame")
   if len(sys.argv) == 2:
      try:
         level = int(sys.argv[1])
      except ValueError:
         print "level must be int"
         sys.exit()
   else:
      level = 1
   dmm = DarkMazeMinigame(screen, level)
   dmm.run()
